package com.tbit.uqbike.util;


import io.netty.buffer.ByteBuf;
import org.apache.log4j.Logger;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

/**
 * Created by John on 2016/12/21.
 */
public class ProtocolUtil {
    private static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ProtocolUtil.class);

    static {
        Calendar calendar = Calendar.getInstance();//获取当前日历对象
        long unixTime = calendar.getTimeInMillis();//获取当前时区下日期时间对应的时间戳
        TIME_ZONE = (int) (unixTime - TimeZone.getDefault().getRawOffset());//获取标准格林尼治时间下日期时间对应的时间戳
    }

    /**
     * 时区 单位毫秒
     */
    public static int TIME_ZONE = 0;
    public static final int UnShort = 65536;
    /**
     * 日志对象
     */
    private static Logger LOG = Logger.getLogger(ProtocolUtil.class);

    /**
     * 常规定位器从字节流提取字符串
     *
     * @param data
     * @param version
     * @return
     */
    public static String GetStringFromByte(byte[] data, String version) {
        // 从前到后找第5个豆号
        try {
            int iStart = getIndex(data, (byte) 0x2C, 5) + 1;
            if (iStart > 0) {
                // 提取短信内容部分
                byte[] bySrc = Arrays.copyOfRange(data, iStart, data.length - iStart);

                // 看看有多少个5D
                int i5D = 0;
                for (int i = 0; i < bySrc.length - 2; i++) {
                    if (bySrc[i] == 0x89 && bySrc[i + 1] == 0x9A && bySrc[i + 2] == 0xAE) {
                        i5D++;
                    }
                }

                // 根据5D的个数，算出总长度
                byte[] byDes = new byte[bySrc.length - 2 * i5D];
                int iDes = 0;

                // 转译回其中包含']',即0x5D
                for (int i = 0; i < bySrc.length; i++) {
                    if (i < bySrc.length - 2 && bySrc[i] == 0x89 && bySrc[i + 1] == 0x9A && bySrc[i + 2] == 0xAE) {
                        byDes[iDes++] = 0x5D;
                        i += 2;
                    } else {
                        byDes[iDes++] = bySrc[i];
                    }
                }

                // 如果是国际版本，并且不全是ASCII字符，就要用UNICODE转换
                if (version.startsWith("W")) {
                    if (isAscii(byDes)) {
                        return new String(byDes, 0, byDes.length, CharsetName.US_ASCII);
                    } else {
                        return new String(byDes, 0, byDes.length, CharsetName.UTF_16BE);
                    }
                } else {
                    if (isAscii(byDes)) {
                        return new String(byDes, 0, byDes.length, CharsetName.US_ASCII);
                    } else if (isGB2312(byDes)) {
                        return new String(byDes, 0, byDes.length, CharsetName.GB2312);
                    } else {
                        return new String(byDes, 0, byDes.length, CharsetName.UTF_16BE);
                    }
                }
            }
        } catch (Exception e) {
            LOG.error("GetStringFromByte", e);
        }

        return StringUtil.Empty;
    }

    /**
     * 伪基站专用设备从字节流提取字符串
     *
     * @param data
     * @param version
     * @return
     */
    public static String GetStringFromByte_Sms(byte[] data, String version) {
        // 从前到后找第7个豆号
        try {
            int iStart = getIndex(data, (byte) 0x2C, 7) + 1;
            if (iStart > 0) {
                // 提取短信内容部分
                byte[] bySrc = Arrays.copyOfRange(data, iStart, data.length - iStart);

                // 看看有多少个5D
                int i5D = 0;
                for (int i = 0; i < bySrc.length - 2; i++) {
                    if (bySrc[i] == 0x89 && bySrc[i + 1] == 0x9A && bySrc[i + 2] == 0xAE) {
                        i5D++;
                    }
                }

                // 根据5D的个数，算出总长度
                byte[] byDes = new byte[bySrc.length - 2 * i5D];
                int iDes = 0;

                // 转译回其中包含']',即0x5D
                for (int i = 0; i < bySrc.length; i++) {
                    if (i < bySrc.length - 2 && bySrc[i] == 0x89 && bySrc[i + 1] == 0x9A && bySrc[i + 2] == 0xAE) {
                        byDes[iDes++] = 0x5D;
                        i += 2;
                    } else {
                        byDes[iDes++] = bySrc[i];
                    }
                }

                if (version.startsWith("W")) {
                    if (isAscii(byDes)) {
                        return new String(byDes, 0, byDes.length, CharsetName.US_ASCII);
                    } else {
                        return new String(byDes, 0, byDes.length, CharsetName.UTF_16BE);
                    }
                } else {
                    if (isAscii(byDes)) {
                        return new String(byDes, 0, byDes.length, CharsetName.US_ASCII);
                    } else if (isGB2312(byDes)) {
                        return new String(byDes, 0, byDes.length, CharsetName.GB2312);
                    } else {
                        return new String(byDes, 0, byDes.length, CharsetName.UTF_16BE);
                    }
                }
            }
        } catch (Exception e) {
            LOG.error("GetStringFromByte", e);
        }

        return StringUtil.Empty;
    }

    /**
     * 找第n个字符的位置
     *
     * @param data
     * @param c
     * @param n
     * @return
     */
    private static int getIndex(byte[] data, byte c, int n) {
        int iPos = 0;
        for (int i = 0; i < data.length; i++) {
            if (data[i] == c) {
                if (iPos++ == n) {
                    return i;
                }
            }
        }
        return -1;
    }

    /**
     * 从字符串取字节流
     *
     * @param content
     * @param bUnicode
     * @return
     */
    public static byte[] GetByteFromString(String content, boolean bUnicode) {
        try {
            byte[] byContent = null;
            if (bUnicode) {
                byContent = content.getBytes(CharsetName.UTF_16BE);
            } else {
                byContent = content.getBytes(CharsetName.GB2312);
            }

            // 看看有多少个5D
            int i5D = 0;
            for (byte by : byContent) {
                if (by == 0x5D) {
                    i5D++;
                }
            }

            // 如果没有，无须转译
            if (i5D == 0) {
                return byContent;
            }
            // 有5D则要转译
            else {
                // 根据5D的个数，算出总长度
                byte[] byDes = new byte[byContent.length + 2 * i5D];
                int iDes = 0;

                // 替换掉其中包含']',即0x5D
                for (byte by : byContent) {
                    if (by == 0x5D) {
                        byDes[iDes++] = (byte) 0x89;
                        byDes[iDes++] = (byte) 0x9A;
                        byDes[iDes++] = (byte) 0xAE;
                    } else {
                        byDes[iDes++] = by;
                    }
                }

                return byDes;
            }
        } catch (Exception e) {
            LOG.error("GetByteFromString", e);
            return null;
        }
    }

    /**
     * 判断是否全是ASCII码
     *
     * @param data
     * @return
     */
    private static boolean isAscii(byte[] data) {
        //循环所有字符
        int i = 0;
        for (byte c : data) {
            i = c & 0xff;
            if (i == '\n' || i == '\t' || i == '\r')   // 换行等字符可以
            {
                continue;
            }

            if (i > 127 || i < 32)   // 其它不可见字符不行
            {
                return false;
            }
        }

        return true;
    }

    /**
     * 确认该字符串是GB2312编码还是Unicode编码
     *
     * @param data
     * @return
     */
    private static boolean isGB2312(byte[] data) {
        // gb2312 高位 0xA1-0xFE
        if (data.length > 0) {
            int i = data[0] & 0xff;
            if (i >= 0xa1 && i <= 0xfe) {
                return true;
            }
        }
        return false;
    }

    /**
     * @param in
     * @param inv
     * @return
     */
    public static String BinGetBcd(ByteBuf in, int inv, int len, char ch) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < len; i++) {
            sb.append(ByteUtil.ByteToHexString(in.getByte(inv + i)));
        }
        String str = sb.toString();
        return StringUtil.TrimEnd(str, ch);
    }

    public static String BinGetBcd(ByteBuf in, int inv, int len) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < len; i++) {
            sb.append(ByteUtil.ByteToHexString(in.getByte(inv + i)));
        }
        return sb.toString();
    }

    public static byte[] BinGetBcdBytes(String str, int len) {
        str = str.toUpperCase();
        byte[] data = new byte[len];
        for (int i = 0; i < len; i++) {
            data[i] = (byte) 0xff;
        }
        String temp = StringUtil.Empty;
        for (int i = 0; (i << 1) < str.length(); i++) {
            temp = str.substring(i << 1, str.length());
            if (temp.length() == 1) {
                temp = temp + "F";
            }
            data[i] = ByteUtil.HexStringToByte(temp);
        }
        int ll = len * 2 - str.length();
        if (ll >= 0) {
            for (int i = 0; i < ll; i++) {
                str += "F";
            }

        } else {
            for (int i = 0; i < len; i++) {
                data[i] = (byte) 0xff;
            }
        }
        return data;
    }

    public static String BinGetTimeStr(ByteBuf in, int inv) {
        long itime = (in.getInt(inv) & 0xffffffffL) * 1000;
        return DateUtil.ymdHmsDU.format(new Date(itime));
    }

    public static Date BinGetTime(ByteBuf in, int inv) {
        long itime = (in.getInt(inv) & 0xffffffffL) * 1000;
        return new Date(itime);
    }

    public static double BinGetLatLon(ByteBuf in, int inv) {
        int i = in.getInt(inv);
        return i / (30000.0 * 60);
    }

    public static String BinGetCellid(ByteBuf in, int inv) {
        int mcc = in.getUnsignedShort(inv);
        inv += 2;
        int mnc = in.getUnsignedShort(inv);
        inv += 2;
        int lac = in.getInt(inv);
        inv += 4;
        int cellid = in.getInt(inv);
        return String.format("%d.%d.%d.%d", mcc, mnc, lac, cellid);
    }

    public static String BinGetString(ByteBuf in, int inv, int len, String charsetname) {
        byte[] data = new byte[len];
        in.getBytes(inv, data);
        try {
            return new String(data, 0, len, charsetname);
        } catch (UnsupportedEncodingException e) {
            return StringUtil.Empty;
        }
    }

    public static String BinGetAsciiStringTrimZero(ByteBuf in, int inv, int len) {
        byte[] data = new byte[len];
        in.getBytes(inv, data);
        int i = len - 1;
        for (; i >= 0; i--) {
            if (data[i] != 0) {
                break;
            }
        }
        return new String(data, 0, i + 1);
    }

    public static String BinGetAsciiString(ByteBuf in, int inv, int len) {
        String str = StringUtil.Empty;
        byte[] data = new byte[len];
        in.getBytes(inv, data);
        try {
            str = new String(data, CharsetName.US_ASCII);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return str;
    }


    //region crctable
    public final static int[] crctab16 =
            {
                    0X0000, 0X1189, 0X2312, 0X329B, 0X4624, 0X57AD, 0X6536, 0X74BF,
                    0X8C48, 0X9DC1, 0XAF5A, 0XBED3, 0XCA6C, 0XDBE5, 0XE97E, 0XF8F7,
                    0X1081, 0X0108, 0X3393, 0X221A, 0X56A5, 0X472C, 0X75B7, 0X643E,
                    0X9CC9, 0X8D40, 0XBFDB, 0XAE52, 0XDAED, 0XCB64, 0XF9FF, 0XE876,
                    0X2102, 0X308B, 0X0210, 0X1399, 0X6726, 0X76AF, 0X4434, 0X55BD,
                    0XAD4A, 0XBCC3, 0X8E58, 0X9FD1, 0XEB6E, 0XFAE7, 0XC87C, 0XD9F5,
                    0X3183, 0X200A, 0X1291, 0X0318, 0X77A7, 0X662E, 0X54B5, 0X453C,
                    0XBDCB, 0XAC42, 0X9ED9, 0X8F50, 0XFBEF, 0XEA66, 0XD8FD, 0XC974,
                    0X4204, 0X538D, 0X6116, 0X709F, 0X0420, 0X15A9, 0X2732, 0X36BB,
                    0XCE4C, 0XDFC5, 0XED5E, 0XFCD7, 0X8868, 0X99E1, 0XAB7A, 0XBAF3,
                    0X5285, 0X430C, 0X7197, 0X601E, 0X14A1, 0X0528, 0X37B3, 0X263A,
                    0XDECD, 0XCF44, 0XFDDF, 0XEC56, 0X98E9, 0X8960, 0XBBFB, 0XAA72,
                    0X6306, 0X728F, 0X4014, 0X519D, 0X2522, 0X34AB, 0X0630, 0X17B9,
                    0XEF4E, 0XFEC7, 0XCC5C, 0XDDD5, 0XA96A, 0XB8E3, 0X8A78, 0X9BF1,
                    0X7387, 0X620E, 0X5095, 0X411C, 0X35A3, 0X242A, 0X16B1, 0X0738,
                    0XFFCF, 0XEE46, 0XDCDD, 0XCD54, 0XB9EB, 0XA862, 0X9AF9, 0X8B70,
                    0X8408, 0X9581, 0XA71A, 0XB693, 0XC22C, 0XD3A5, 0XE13E, 0XF0B7,
                    0X0840, 0X19C9, 0X2B52, 0X3ADB, 0X4E64, 0X5FED, 0X6D76, 0X7CFF,
                    0X9489, 0X8500, 0XB79B, 0XA612, 0XD2AD, 0XC324, 0XF1BF, 0XE036,
                    0X18C1, 0X0948, 0X3BD3, 0X2A5A, 0X5EE5, 0X4F6C, 0X7DF7, 0X6C7E,
                    0XA50A, 0XB483, 0X8618, 0X9791, 0XE32E, 0XF2A7, 0XC03C, 0XD1B5,
                    0X2942, 0X38CB, 0X0A50, 0X1BD9, 0X6F66, 0X7EEF, 0X4C74, 0X5DFD,
                    0XB58B, 0XA402, 0X9699, 0X8710, 0XF3AF, 0XE226, 0XD0BD, 0XC134,
                    0X39C3, 0X284A, 0X1AD1, 0X0B58, 0X7FE7, 0X6E6E, 0X5CF5, 0X4D7C,
                    0XC60C, 0XD785, 0XE51E, 0XF497, 0X8028, 0X91A1, 0XA33A, 0XB2B3,
                    0X4A44, 0X5BCD, 0X6956, 0X78DF, 0X0C60, 0X1DE9, 0X2F72, 0X3EFB,
                    0XD68D, 0XC704, 0XF59F, 0XE416, 0X90A9, 0X8120, 0XB3BB, 0XA232,
                    0X5AC5, 0X4B4C, 0X79D7, 0X685E, 0X1CE1, 0X0D68, 0X3FF3, 0X2E7A,
                    0XE70E, 0XF687, 0XC41C, 0XD595, 0XA12A, 0XB0A3, 0X8238, 0X93B1,
                    0X6B46, 0X7ACF, 0X4854, 0X59DD, 0X2D62, 0X3CEB, 0X0E70, 0X1FF9,
                    0XF78F, 0XE606, 0XD49D, 0XC514, 0XB1AB, 0XA022, 0X92B9, 0X8330,
                    0X7BC7, 0X6A4E, 0X58D5, 0X495C, 0X3DE3, 0X2C6A, 0X1EF1, 0X0F78,
            };
    //endregion

    /**
     * 计算出crc 校验值
     *
     * @param in
     * @param inv
     * @param len
     * @return
     */
    public static short GetCrcByteArray(ByteBuf in, int inv, int len) {
        int fcs = 0xffff;
        int tempI = 0;
        for (int i = 0; i < len; i++) {
            tempI = in.getByte(inv + i) & 0xff;
            fcs = ((fcs >> 8) ^ crctab16[(fcs ^ tempI) & 0xff]);
        }
        return (short) ((~fcs) & 0xffff);
    }

    public static short GetCrcByteArray(byte[] data, int inv, int len) {
        int fcs = 0xffff;
        int tempI = 0;
        for (int i = 0; i < len; i++) {
            tempI = data[inv + i] & 0xff;
            fcs = ((fcs >> 8) ^ crctab16[(fcs ^ tempI) & 0xff]);
        }
        return (short) ((~fcs) & 0xffff);
    }

    public static void main(String[] args) {
        byte[] bs = new byte[]{(byte)0xAA,(byte)0xAA,(byte)0x00,(byte)0x11,(byte)0x04,(byte)0x81,(byte)0x2C,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x5E,(byte)0x79,(byte)0xFD,(byte)0x84,(byte)0x32,(byte)0x54};
        System.out.println(GetCrcByteArray(bs, 2, bs.length - 2 - 2));
    }
}
